// Copyright (c) 2025, 大连理工大学 (Dalian University of Technology)
//
// Licensed under the Mulan PSL v2.
// You can use this file according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of the License at
//
//     http://license.coscl.org.cn/MulanPSL2
//
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. SEE THE MULAN PSL v2
// FOR MORE DETAILS.

#include "DBManager/Repository/HDF5/Input/Element/NIdMapEIdsRepository.h"

#include "DBManager/IRepository/Input/Property/IPropertySummaryRepository.h"
#include "DBManager/Repository/HDF5/Base/HDF5RepositoryMacro.h"

HDF5_REG_REPOSITORY (NIdMapEIdsRepository)

using namespace SG::DBManager::HDF5;

using SG::DataStructure::FEM::NIdMapEidsData;
using SG::DBManager::HDF5::Element::NIdMapEIds;

NIdMapEIdsRepository::NIdMapEIdsRepository (HDF5Database& database) : MainlyRepositoryBase<NIdMapEidsData> (database)
{}

void NIdMapEIdsRepository::InitialDatasetInfo ()
{
    RegisterTableInfo<SG::DataStructure::Common::Id> ("/OWN/INDEX/INPUT/ELEMENT/NIDMAPEIDS", IndexDataset);
    RegisterTableInfo<NIdMapEIds> ("/OWN/INPUT/ELEMENT/NIDMAPEIDS/IDENTITY", MainDataset);
    RegisterTableInfo<SG::DataStructure::Common::Id> ("/OWN/INPUT/ELEMENT/NIDMAPEIDS/INFO", INFO);
}

void NIdMapEIdsRepository::DumpWriteCahce ()
{
    auto& writeStorage = this->GetMainCache ().m_writeStorage;

    // 设置为无顺序
    this->GetMainDatasetInfo ().SetIsSequential (false);

    // 主表数据
    std::vector<SG::DBManager::HDF5::Element::NIdMapEIds> IDENTITYData{};
    // 附表数据
    std::vector<SG::DataStructure::Common::Id> INFOData;

    // 转换为nastran格式数据
    OwnToNastran (writeStorage, IDENTITYData, INFOData);

    // 写出主表
    WriteData (MainDataset, IDENTITYData);
    // 写出附表
    WriteData (INFO, INFOData);
    // 更新Index表
    WriteIndexData (this->GetIndexDatasetInfo (), writeStorage);

    // 清空缓存
    writeStorage.clear ();
}

std::vector<NIdMapEIdsRepository::DataType>*  NIdMapEIdsRepository::LoadReadCache (std::size_t loadIndex)
{
    // 读取范围内的主表信息
    std::vector<NIdMapEIds> IDENTITYData;
    this->LoadMainMetadata (loadIndex, IDENTITYData);

    if (IDENTITYData.empty ())
    {
        return nullptr;
    }

    // 读取范围内的附表信息
    std::vector<SG::DataStructure::Common::Id> INFOData;
    LoadSecondaryMetadata (IDENTITYData, INFO_POS, INFO_LEN, INFO, INFOData);

    auto* cacheVector = m_cacheHelper.LookupOrCreateVector<DataType> (GetMainCache ().m_readCacheKey, IDENTITYData.size ());
    NastranToOwn (IDENTITYData, INFOData, *cacheVector);
    return cacheVector;
}

void NIdMapEIdsRepository::SortData ()
{
    std::vector<NIdMapEIds> IDENTITYData;
    GetDataset (MainDataset).read (IDENTITYData);

    if (IDENTITYData.empty ())
    {
        GetMainDatasetInfo ().SetIsSequential (true);
        return;
    }

    std::vector<SG::DataStructure::Common::Id> INFOData;
    GetDataset (INFO).read (INFOData);

    // 将所有数据转换
    std::vector<NIdMapEidsData> AllData{};
    NastranToOwn (IDENTITYData, INFOData, AllData);
    // 排序
    std::sort (AllData.begin (), AllData.end ());

    OwnToNastran (AllData, IDENTITYData, INFOData, true);

    // 写出数据至文件
    GetDataset (MainDataset).write (IDENTITYData);
    GetDataset (INFO).write (INFOData);

    // 重写索引
    RewriteIndexData (AllData);

    // 设置为已顺序
    GetMainDatasetInfo ().SetIsSequential (true);
}

std::vector<NIdMapEidsData> NIdMapEIdsRepository::GetAllData ()
{
    Sort ();

    std::vector<NIdMapEidsData> result;

    std::vector<NIdMapEIds> IDENTITYData;
    GetDataset (MainDataset).read (IDENTITYData);

    if (IDENTITYData.empty ())
    {
        return result;
    }

    std::vector<SG::DataStructure::Common::Id> INFOData;
    GetDataset (INFO).read (INFOData);

    // 将所有数据转换
    NastranToOwn (IDENTITYData, INFOData, result);

    return result;
}

void NIdMapEIdsRepository::OwnToNastran (std::vector<SG::DataStructure::FEM::NIdMapEidsData>&        own,
                                         std::vector<SG::DBManager::HDF5::Element::NIdMapEIds>& _OUT IDENTITYData,
                                         std::vector<SG::DataStructure::Common::Id>& _OUT            INFOData,
                                         bool                                                        isSort)
{
    INFOData.clear ();
    IDENTITYData.clear ();
    IDENTITYData.reserve (own.size ());

    // 获取已有数据条数，主要是放在主表的POS字段
    // 如果是重新排序的时候，Pos都是从0 开始的
    size_t INFOCount = isSort ? 0 : this->GetDatasetInfo (INFO).GetRowCount ();

    // OwnIdent 代表咱们自己的数据结构
    // NasIdent 代表nastran的数据结构
    for (auto& OwnIdent : own)
    {
        // 写入附表
        INFOData.insert (INFOData.end (), OwnIdent.m_elementIds.begin (), OwnIdent.m_elementIds.end ());

        const size_t len = OwnIdent.m_elementIds.size ();
        // 写入IDENTITY
        NIdMapEIds identity{};
        identity.NID      = OwnIdent.m_id;
        identity.INFO_POS = static_cast<SG::DataStructure::Common::I64> (INFOCount);
        identity.INFO_LEN = static_cast<SG::DataStructure::Common::I64> (len);

        IDENTITYData.emplace_back (identity);

        INFOCount += len;  // 加1是因为nsm
    }
}

void NIdMapEIdsRepository::NastranToOwn (std::vector<SG::DBManager::HDF5::Element::NIdMapEIds>&    IDENTITYData,
                                         std::vector<SG::DataStructure::Common::Id>&               INFOData,
                                         std::vector<SG::DataStructure::FEM::NIdMapEidsData>& _OUT own)
{
    own.clear ();
    own.reserve (IDENTITYData.size ());

    // 代表初始的Index 位置, 因为数据有可能是截取了附表的一部分，所有主表中根据POS找的时候需要对应一下，
    // 比如第一个主表数据中POS字段为10000， 附表的vector的数据 就是从附表中的第10000个开始对应的 也就是Index==0的
    const SG::DataStructure::Common::I64 infoIndex = IDENTITYData[0].INFO_POS;

    for (auto&& NasIdent : IDENTITYData)
    {
        NIdMapEidsData data;
        data.m_id = NasIdent.NID;
        // assign 清除矢量并将指定的元素复制到该空矢量。
        data.m_elementIds.assign (INFOData.begin () + NasIdent.INFO_POS - infoIndex,
                                  INFOData.begin () + NasIdent.INFO_POS + NasIdent.INFO_LEN - infoIndex);

        own.emplace_back (data);
    }
}
